{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b9f3df9e-c2ea-48a4-8ed2-a6402736fb86",
   "metadata": {},
   "outputs": [],
   "source": [
    "# File: hang.ipynb\n",
    "# Code: Claude Code and Codex\n",
    "# Review: Ryoichi Ando (ryoichi.ando@zozo.com)\n",
    "# License: Apache v2.0"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "950b418e-2ff8-429c-97b1-79f87304af40",
   "metadata": {},
   "outputs": [],
   "source": [
    "from frontend import App\n",
    "\n",
    "# create an app\n",
    "app = App.create(\"hang\")\n",
    "\n",
    "# make a sheet and add to the asset\n",
    "V, F = app.mesh.square(res=128, ex=[1, 0, 0], ey=[0, 1, 0])\n",
    "app.asset.add.tri(\"sheet\", V, F)\n",
    "\n",
    "# make a hanging sheet scene\n",
    "scene = app.scene.create()\n",
    "\n",
    "# hang a sheet and pin top two corners\n",
    "sheet = scene.add(\"sheet\").direction([1, 0, 0], [0, 1, 0])\n",
    "sheet.pin(sheet.grab([-1, 1, 0]) + sheet.grab([1, 1, 0]))\n",
    "\n",
    "# set strain-limiting upper bound\n",
    "sheet.param.set(\"strain-limit\", 0.01)\n",
    "\n",
    "# build the scene and preview\n",
    "scene = scene.build().report()\n",
    "scene.preview()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "46a61c8b-126b-4e0f-8c11-cfc1cb465fab",
   "metadata": {},
   "outputs": [],
   "source": [
    "import random  # noqa\n",
    "\n",
    "# set small jitter\n",
    "jitter_time = 0.1 + 0.5 * random.random()\n",
    "\n",
    "# build a new session\n",
    "session = app.session.create(scene)\n",
    "\n",
    "# obtain default gravity value\n",
    "gravity = session.param.get(\"gravity\")\n",
    "\n",
    "# set the maximal video frame to 200\n",
    "session.param.set(\"frames\", 200)\n",
    "\n",
    "# set dynamic gravity\n",
    "(\n",
    "    session.param.dyn(\"gravity\")  # focus on gravity\n",
    "    .time(1)  # move the time cursor to 1 second\n",
    "    .hold()  # hold the current value\n",
    "    .time(1 + jitter_time)  # move the time cursor slightly forward\n",
    "    .change(-gravity)  # flip the gravity\n",
    "    .time(2.0)  # move the time cursor to 2 seconds\n",
    "    .change(gravity)  # flip back the gravity\n",
    ")\n",
    "\n",
    "session = session.build()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d9c3f1b7-bc8d-4c49-8d83-9b0ce95b9c95",
   "metadata": {},
   "outputs": [],
   "source": [
    "session.start().preview()\n",
    "session.stream()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "45fa755d-8e22-41e3-8a91-0e9dcbd8ee14",
   "metadata": {},
   "outputs": [],
   "source": [
    "# run this cell after sufficnt frames are simulated\n",
    "session.animate()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a01ce8aa-e0bb-4f38-bb43-d148b3222194",
   "metadata": {},
   "outputs": [],
   "source": [
    "# export all simulated frames and make a zip file\n",
    "session.export.animation().zip()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "546a1545-2238-4512-b88c-27d4d0a0b2ec",
   "metadata": {},
   "outputs": [],
   "source": [
    "# this is for CI\n",
    "if app.ci:\n",
    "    assert session.finished()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
